﻿using LibBinary.FileDeclaration.Text;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LibBinary.Serialization.BinaryInText
{
    public class DeclarationProvider : IObjectDeclarationProvider
    {
        private static DeclarationProvider _Instance = new DeclarationProvider();
        public static DeclarationProvider Instance { get { return _Instance; } }

        private DeclarationProvider()
        {
        }

        public StructuredDeclaration Get(string name)
        {
            return declarationDict[name];
        }

        private static Dictionary<string, StructuredDeclaration> declarationDict = new Dictionary<string, StructuredDeclaration>();

        static DeclarationProvider()
        {
            declarationDict.Add("array", MakeArray());
            declarationDict.Add("conditional", MakeConditional());
            declarationDict.Add("constant-bool", MakeConstant(PrimitiveType.Boolean));
            declarationDict.Add("constant-byte", MakeConstant(PrimitiveType.Byte));
            declarationDict.Add("constant-int16", MakeConstant(PrimitiveType.Int16));
            declarationDict.Add("constant-int32", MakeConstant(PrimitiveType.Int32));
            declarationDict.Add("primitive", MakePrimitive());
            declarationDict.Add("reference", MakeRelativeReference());
            declarationDict.Add("string", MakeString());
            declarationDict.Add("structured", MakeStructured());
        }

        private static StructuredDeclaration MakeArray()
        {
            return new StructuredDeclaration(new StructuredField[] {
                new StructuredField
                {
                    FieldName = "length",
                    Declaration = new ObjectDeclaration(LengthProviderProvider.Instance),
                },
                new StructuredField
                {
                    FieldName = "item",
                    Declaration = new ObjectDeclaration(Instance),
                }
            });
        }

        private static StructuredDeclaration MakeConditional()
        {
            return new StructuredDeclaration(new StructuredField[] {
                new StructuredField
                {
                    FieldName = "condition",
                    Declaration = new ObjectDeclaration(ConditionProviderProvider.Instance),
                },
                new StructuredField
                {
                    FieldName = "child",
                    Declaration = new ObjectDeclaration(Instance),
                }
            });
        }

        private static StructuredDeclaration MakeConstant(PrimitiveType type)
        {
            return new StructuredDeclaration(new StructuredField[] {
                new StructuredField
                {
                    FieldName = "value",
                    Declaration = new PrimitiveDeclaration(type),
                },
            });
        }

        private static StructuredDeclaration MakePrimitive()
        {
            return new StructuredDeclaration(new StructuredField[] {
                new StructuredField
                {
                    FieldName = "type",
                    Declaration = new StringDeclaration(),
                }
            });
        }

        private static StructuredDeclaration MakeRelativeReference()
        {
            return new StructuredDeclaration(new StructuredField[] {
                new StructuredField
                {
                    FieldName = "path",
                    Declaration = new ArrayDeclaration(new PrimitiveDeclaration(PrimitiveType.Int32)),
                }
            });
        }

        private static StructuredDeclaration MakeString()
        {
            return new StructuredDeclaration(new StructuredField[] {
                new StructuredField
                {
                    FieldName = "length",
                    Declaration = new ObjectDeclaration(LengthProviderProvider.Instance),
                },
                new StructuredField
                {
                    FieldName = "encoding",
                    Declaration = new StringDeclaration(),
                }
            });
        }

        private static StructuredDeclaration MakeStructured()
        {
            return new StructuredDeclaration(new StructuredField[] {
                new StructuredField
                {
                    FieldName = "children",
                    Declaration = new ArrayDeclaration(new ObjectDeclaration(Instance)),
                }
            });
        }
    }
}
